package coderead.plugin;
/**
 * @Copyright 源码阅读网 http://coderead.cn
 */

import com.intellij.icons.AllIcons;
import com.intellij.notification.NotificationDisplayType;
import com.intellij.notification.NotificationGroup;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.MouseChecker;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.*;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.Alarm;
import com.intellij.util.Consumer;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.tree.TreeUtil;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 鲁班大叔
 * @date 2021
 */
public class SearchPopupFrameUI extends AbstractSearchPopupUI implements MouseChecker {
    private static final Logger LOG = Logger.getInstance(SearchPopupFrameUI.class);
    private SETab mySelectedTab;
    private final List<SETab> myTabs = new ArrayList<>();
    // 定住悬浮窗
    private boolean pinWindow;
    private JBPopup jbPopup;
    protected Alarm myAlarm;
    private boolean initComplete = false;

    public SearchPopupFrameUI(Project project) {
        super(project);
        init();// 初始化UI
        initSearchActions();// 初始化监听事件
        updateViewType(ViewType.FULL);
        myAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, this);
        switchToTab(myTabs.get(0));
        initComplete = true;
    }

    public SearchPopupFrameUI(Project project, String searchText, String activeTab) {
        super(project);

        init();// 初始化UI
        initSearchActions();// 初始化监听事件
        updateViewType(ViewType.FULL);
        myAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, this);
        // 切换选中
        myTabs.stream()
                .filter(f -> f.getText().equals(activeTab))
                .findFirst().ifPresent(this::switchToTab);
        if (StringUtil.isNotEmpty(searchText)) {
            getSearchField().setText(searchText);
        }
        initComplete = true;

    }

    @Override
    public Tree createList() {
        Tree tree = new Tree();
        tree.setRootVisible(false);
        return tree;
    }


    @Override
    protected JPanel createTopLeftPanel() {
        JPanel jPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
        jPanel.setOpaque(false);
        SETab JarTab = new SETab("jar", new ArtifactSearchHandler(this, myResultsList));
        jPanel.add(JarTab);
        myTabs.add(JarTab);

        SETab classTab = new SETab("class", new ClassSearchHandler(this, myResultsList));
        jPanel.add(classTab);
        myTabs.add(classTab);


        return jPanel;
    }

    @Override
    protected JPanel createSettingsPanel() {
        JPanel res = new JPanel();
        BoxLayout bl = new BoxLayout(res, BoxLayout.X_AXIS);
        res.setLayout(bl);
        res.setOpaque(false);
        /*JBCheckBox myNonProjectCB = new JBCheckBox();
        myNonProjectCB.setOpaque(false);
        myNonProjectCB.setFocusable(false);
        myNonProjectCB.setText("选项卡");
        res.add(myNonProjectCB);*/
        DefaultActionGroup actionGroup = new DefaultActionGroup();
        actionGroup.addAction(new FixedWindowAction());
        ActionToolbar toolbar = ActionManager.getInstance()
                .createActionToolbar(ActionPlaces.EDITOR_POPUP + "maven.search.toolbar", actionGroup, true);
        toolbar.setLayoutPolicy(ActionToolbar.NOWRAP_LAYOUT_POLICY);
        toolbar.updateActionsImmediately();
        JComponent toolbarComponent = toolbar.getComponent();
        toolbarComponent.setOpaque(false);
        toolbarComponent.setBorder(JBUI.Borders.empty(2, 18, 2, 9));
        res.add(toolbarComponent);
        return res;
    }

    private volatile String defaultDetailsText = "The service comes from <a href='https://mvn.coderead.cn/'>coderead.cn</a>";

    @Override
    protected String getInitialHint() {
        return PluginContext.getConfig("open.details", defaultDetailsText);
    }

    @Override
    public void dispose() {
        jbPopup.cancel(); //关闭
    }

    // todo 读取项目描述结果集中不存在描述
    public void setDescribeText(String text) {
        text = text == null ? "" : text;
        myHintLabel.setAndUpdateText(text);
        myHintLabel.updateUI();
    }

    public void setStateText() {
        myResultsList.getEmptyText().setText("searching...");
    }

    @Override
    public boolean check(MouseEvent event) {
        //pinWindow
        return event.getID() == MouseEvent.MOUSE_PRESSED && !pinWindow;
    }

    public boolean isPinWindow() {
        return pinWindow;
    }


    private void switchToNextTab() {
        int currentIndex = myTabs.indexOf(mySelectedTab);
        SETab nextTab = currentIndex == myTabs.size() - 1 ? myTabs.get(0) : myTabs.get(currentIndex + 1);
        switchToTab(nextTab);
    }

    private void switchToPrevTab() {
        int currentIndex = myTabs.indexOf(mySelectedTab);
        SETab prevTab = currentIndex == 0 ? myTabs.get(myTabs.size() - 1) : myTabs.get(currentIndex - 1);
        switchToTab(prevTab);
    }

    private void switchToTab(SETab tab) {
        mySelectedTab = tab;
        myResultsList.setModel(tab.handler.getTreeModel());
        myResultsList.setCellRenderer(tab.handler.getTreeCellRenderer());
        if (initComplete) {
            scheduleSearch();
        }
        repaint();
    }


    private void initSearchActions() {
        //切换选项卡
        create(e -> switchToNextTab())
                .registerCustomShortcutSet(KeyEvent.VK_TAB, 0, this);
        // 搜索输入
        mySearchField.addDocumentListener(new DocumentAdapter() {
            @Override
            protected void textChanged(DocumentEvent e) {
                scheduleSearch();
            }
        });
        // 选中节点
        create(e -> {
            TreePath selectionPath = myResultsList.getSelectionPath();
            if (myResultsList.isCollapsed(selectionPath)) {
                myResultsList.expandPath(selectionPath);
            } else {
                myResultsList.collapsePath(selectionPath);
            }
            mySelectedTab.handler.actionItem(selectionPath);

        }).registerCustomShortcutSet(KeyEvent.VK_ENTER, 0, getSearchField());
        myResultsList.addTreeExpansionListener(new TreeExpansionListener() {
            @Override
            public void treeExpanded(TreeExpansionEvent event) {
                int row = myResultsList.getRowForPath(event.getPath());
                int top = row - 1;
                int bottom = TreeUtil.getVisibleRowCount(myResultsList) + top - 1;
                // 显示在第一行
                TreeUtil.showAndSelect(myResultsList, row, bottom, row, -1, false, true, false);
            }

            @Override
            public void treeCollapsed(TreeExpansionEvent event) {

            }
        });

        myResultsList.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                TreePath path = myResultsList.getPathForLocation(e.getX(), e.getY());
                mySelectedTab.handler.actionItem(path);
            }
        });
        myResultsList.addTreeSelectionListener(new TreeSelectionListener() {
            @Override
            public void valueChanged(TreeSelectionEvent e) {
                mySelectedTab.handler.selectItem(e.getPath());
            }
        });


    }

    public interface SearchHandler {
        TreeModel getTreeModel();

        TreeCellRenderer getTreeCellRenderer();

        void processSearch(String keyWord) throws IOException;

        default void actionItem(TreePath path) {
        }

        default void selectItem(TreePath path) {
        }

        default String getTip() {
            return null;
        }
    }


    public void setJbPopup(JBPopup jbPopup) {
        this.jbPopup = jbPopup;
    }

    public void scheduleSearch() {
        String text = this.mySearchField.getText();
        if ("".equals(text) && myResultsList.getRowCount() == 0) {
            return;
        }
        setPaintBusy(true);
        myResultsList.getEmptyText().setText("Searching...");

        SearchHandler handler = mySelectedTab.handler;

        this.myAlarm.cancelAllRequests();
        this.myAlarm.addRequest(() -> {
            try {
                handler.processSearch(text);
            } catch (Throwable e) {
                LOG.error(e);
            } finally {
                setPaintBusy(false);
                String emptyTip = "Nothing to show";
                if (StringUtil.isNotEmpty(text) && mySelectedTab.handler.getTip() != null) {
                    emptyTip = mySelectedTab.handler.getTip();
                }
                myResultsList.getEmptyText().setText(emptyTip);


            }
        }, 300);
    }

    protected void setPaintBusy(boolean busy) {

    }


    protected CopyTextAction createCopyTextAction(String name, String text, Consumer<String> consumer) {
        return new CopyTextAction(name, text, consumer);
    }

    // 文本复制
    protected class CopyTextAction extends AnAction {
        private final Consumer<String> consumer;
        String text;

        public CopyTextAction(String name, String text, Consumer<String> consumer) {
            super(name);
            assert text != null;
            this.text = text;
            this.consumer = consumer;
        }

        @Override
        public void actionPerformed(AnActionEvent e) {
            Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
            Transferable trans = new StringSelection(text);
            clipboard.setContents(trans, null);

            new NotificationGroup("Maven Search copy text",
                    NotificationDisplayType.BALLOON, false).
                    createNotification("已拷贝至剪贴板", null,
                            text, NotificationType.INFORMATION)
                    .notify(SearchPopupFrameUI.this.getMyProject());

            if (!pinWindow) {
                //关闭整个页面
                SearchPopupFrameUI.this.dispose();
            }
            if (consumer != null) {
                consumer.consume(text);
            }
        }
    }

    // 固定悬浮窗口
    public class FixedWindowAction extends ToggleAction {

        public FixedWindowAction() {
            super("pin Window", "pin Window", AllIcons.General.Pin_tab);
            int mask = SystemInfo.isMac ? InputEvent.META_DOWN_MASK : InputEvent.CTRL_DOWN_MASK;
            registerCustomShortcutSet(KeyEvent.VK_D, mask, SearchPopupFrameUI.this);
        }

        @Override
        public boolean isSelected(AnActionEvent e) {
            return pinWindow;
        }

        @Override
        public void setSelected(AnActionEvent e, boolean state) {
            pinWindow = state;
        }
    }

    private class SETab extends JLabel {

        private final SearchHandler handler;

        SETab(String title, SearchHandler handler) {
            super(title);
            this.handler = handler;
            Insets insets = JBUI.insets(0, 12);
            setBorder(JBUI.Borders.empty(insets.top, insets.left, insets.bottom, insets.right));
            addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    switchToTab(SETab.this);
                }
            });
        }


        @Override
        public Dimension getPreferredSize() {
            Dimension size = super.getPreferredSize();
            size.height = JBUI.scale(29);
            return size;
        }

        @Override
        public boolean isOpaque() {
            return mySelectedTab == this;
        }

        @Override
        public Color getBackground() {
            return mySelectedTab == this
                    ? TAB_ACTIVE_BACKGROUND
                    : super.getBackground();
        }

        @Override
        public Color getForeground() {
            return super.getForeground();
        }
    }

}
